"""
日志管理系统
提供多层次日志分类和智能日志管理功能

主要功能：
1. 应用业务日志记录 - 记录正常的业务操作和流程
2. 安全审计日志记录 - 记录敏感操作和权限相关活动
3. 性能监控日志记录 - 记录API响应时间和性能指标
4. 错误异常日志记录 - 记录系统错误和异常情况
5. 数据库查询日志记录 - 记录数据库操作和查询性能

使用方式：
- 直接调用：logger_manager.info("消息", extra={"额外数据": "值"})
- 装饰器方式：@log_api_call("API名称") 或 @log_user_action("操作名称")
"""

import logging
import os
import time
from datetime import datetime
from functools import wraps
from django.conf import settings
from django.utils import timezone
import json


class LoggerManager:
    """
    日志管理器核心类
    
    负责创建和管理5种不同类型的日志记录器：
    1. app_logger - 应用业务日志
    2. security_logger - 安全审计日志
    3. performance_logger - 性能监控日志
    4. error_logger - 错误异常日志
    5. db_logger - 数据库查询日志
    
    每种日志记录器都有独立的文件存储和格式配置
    """
    
    def __init__(self):
        """
        初始化日志管理器
        
        自动调用setup_loggers()方法来创建所有日志记录器
        确保在实例化时就完成所有日志配置
        """
        self.setup_loggers()
    
    def setup_loggers(self):
        """
        设置和配置所有日志记录器
        
        执行步骤：
        1. 创建logs目录（如果不存在）
        2. 为每种日志类型创建专门的记录器
        3. 配置文件路径、日志等级和格式
        
        日志文件存储位置：backend/logs/
        - application.log: 应用业务日志
        - security.log: 安全审计日志  
        - performance.log: 性能监控日志
        - error.log: 错误异常日志
        - database.log: 数据库查询日志
        """
        # 创建日志目录 - 确保logs文件夹存在
        log_dir = os.path.join(settings.BASE_DIR, 'logs')
        os.makedirs(log_dir, exist_ok=True)  # exist_ok=True表示目录已存在时不报错
        
        # 应用业务日志记录器 - 记录正常的业务操作流程
        # 如：用户登录、数据查询、业务处理等
        self.app_logger = self._create_logger(
            'application',  # 日志记录器名称
            os.path.join(log_dir, 'application.log'),  # 日志文件路径
            logging.INFO  # 日志等级：INFO及以上才会记录
        )
        
        # 安全审计日志记录器 - 记录安全相关的敏感操作
        # 如：权限验证、密码修改、敏感数据访问等
        self.security_logger = self._create_logger(
            'security',
            os.path.join(log_dir, 'security.log'),
            logging.INFO
        )
        
        # 性能监控日志记录器 - 记录系统性能指标
        # 如：API响应时间、数据库查询耗时、慢操作检测等
        self.performance_logger = self._create_logger(
            'performance',
            os.path.join(log_dir, 'performance.log'),
            logging.INFO
        )
        
        # 错误异常日志记录器 - 记录系统错误和异常
        # 如：程序异常、系统错误、失败的操作等
        self.error_logger = self._create_logger(
            'error',
            os.path.join(log_dir, 'error.log'),
            logging.ERROR  # 只记录ERROR及以上等级的日志
        )
        
        # 数据库查询日志记录器 - 记录数据库相关操作
        # 如：SQL查询语句、查询耗时、影响行数等
        self.db_logger = self._create_logger(
            'database',
            os.path.join(log_dir, 'database.log'),
            logging.INFO
        )
    
    def _create_logger(self, name, filename, level):
        """
        创建并配置日志记录器
        
        参数说明：
        - name: 日志记录器名称（用于标识不同类型的日志）
        - filename: 日志文件的完整路径
        - level: 日志记录等级（INFO、WARNING、ERROR等）
        
        执行步骤：
        1. 获取或创建指定名称的日志记录器
        2. 设置日志记录器的最低日志等级
        3. 检查是否已有处理器（避免重复添加）
        4. 创建文件处理器并配置编码为UTF-8
        5. 设置日志格式化器（时间、名称、等级、消息）
        6. 将处理器添加到日志记录器
        
        返回值：配置完成的日志记录器对象
        """
        # 步骤1：获取或创建指定名称的日志记录器
        # getLogger会返回已存在的记录器或创建新的记录器
        logger = logging.getLogger(name)
        
        # 步骤2：设置日志记录器的最低等级
        # 只有等级大于等于此值的日志才会被记录
        logger.setLevel(level)
        
        # 步骤3：检查是否已有处理器，避免重复添加导致日志重复输出
        if not logger.handlers:
            # 步骤4：创建文件处理器，指定UTF-8编码确保中文正确显示
            file_handler = logging.FileHandler(filename, encoding='utf-8')
            file_handler.setLevel(level)
            
            # 步骤5：创建并配置日志格式化器
            # 格式：时间 - 记录器名称 - 日志等级 - 日志消息
            # 示例：2024-12-02 14:30:15 - application - INFO - 用户登录成功
            formatter = logging.Formatter(
                '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                datefmt='%Y-%m-%d %H:%M:%S'  # 时间格式：年-月-日 时:分:秒
            )
            file_handler.setFormatter(formatter)
            
            # 步骤6：将文件处理器添加到日志记录器
            logger.addHandler(file_handler)
        
        return logger
    
    def info(self, message, extra=None):
        """
        记录信息级别的业务日志
        
        参数说明：
        - message: 主要日志消息内容
        - extra: 可选的额外信息字典，会被转换为JSON格式
        
        使用场景：
        - 记录正常的业务操作流程
        - 记录用户行为和系统状态变化
        - 记录重要的业务逻辑执行结果
        
        示例：
        logger_manager.info("用户登录成功", {"用户名": "张三", "登录时间": "2024-12-02 14:30:15"})
        """
        # 如果提供了额外信息，将其格式化为JSON并附加到消息中
        if extra:
            # ensure_ascii=False确保中文字符正确显示
            message = f"{message} - 额外信息: {json.dumps(extra, ensure_ascii=False)}"
        
        # 使用应用业务日志记录器记录信息
        self.app_logger.info(message)
    
    def warning(self, message, extra=None):
        """
        记录警告级别的日志
        
        参数说明：
        - message: 警告消息内容
        - extra: 可选的额外信息字典
        
        使用场景：
        - 记录潜在的问题或异常情况
        - 记录性能问题（如慢查询、超时等）
        - 记录配置问题或环境异常
        - 记录用户输入验证失败
        
        示例：
        logger_manager.warning("API响应时间过长", {"耗时": "3.5秒", "接口": "/api/user/list"})
        """
        # 如果提供了额外信息，格式化并附加到警告消息中
        if extra:
            message = f"{message} - 额外信息: {json.dumps(extra, ensure_ascii=False)}"
        
        # 使用应用业务日志记录器记录警告
        self.app_logger.warning(message)
    
    def error(self, message, exception=None, extra=None):
        """
        记录错误级别的日志
        
        参数说明：
        - message: 错误消息内容
        - exception: 可选的异常对象，会自动提取异常信息
        - extra: 可选的额外信息字典
        
        使用场景：
        - 记录系统错误和异常
        - 记录业务逻辑错误
        - 记录数据库操作失败
        - 记录外部API调用失败
        
        特点：
        - 同时写入error.log和application.log两个文件
        - 便于分别查看错误日志和完整业务日志
        
        示例：
        try:
            # 一些可能出错的操作
            pass
        except Exception as e:
            logger_manager.error("用户数据保存失败", exception=e, extra={"用户ID": 123})
        """
        # 如果提供了异常对象，将异常信息附加到消息中
        if exception:
            message = f"{message} - 异常: {str(exception)}"
        
        # 如果提供了额外信息，格式化并附加到消息中
        if extra:
            message = f"{message} - 额外信息: {json.dumps(extra, ensure_ascii=False)}"
        
        # 同时记录到专门的错误日志文件和应用业务日志文件
        # 这样既方便单独查看错误，也能在业务日志中看到完整流程
        self.error_logger.error(message)
        self.app_logger.error(message)
    
    def security_audit(self, action, request, success, details=""):
        """
        记录安全审计日志
        
        参数说明：
        - action: 操作名称（如"用户登录"、"密码修改"、"权限验证"等）
        - request: Django请求对象，用于提取用户和环境信息
        - success: 操作是否成功的布尔值
        - details: 可选的详细信息字符串
        
        使用场景：
        - 记录用户登录/登出操作
        - 记录权限验证和访问控制
        - 记录敏感数据的访问和修改
        - 记录密码修改、用户权限变更等安全相关操作
        
        记录内容：
        - 操作时间（ISO格式）
        - 操作名称和用户信息
        - 客户端IP地址和用户代理
        - 操作结果（成功/失败）
        - 详细信息
        
        示例：
        logger_manager.security_audit("用户登录", request, True, "密码验证成功")
        """
        try:
            # 步骤1：提取用户信息
            # 首先尝试从会话中获取用户名，如果没有则标记为匿名用户
            user_info = "匿名用户"
            if hasattr(request, 'session') and request.session.get('session_data'):
                session_data = request.session.get('session_data')
                user_info = session_data.get('username', '未知用户')
            
            # 步骤2：提取客户端网络信息
            ip_address = self._get_client_ip(request)  # 获取客户端IP地址
            user_agent = request.META.get('HTTP_USER_AGENT', '')  # 获取用户代理字符串
            
            # 步骤3：构建完整的审计日志条目
            log_entry = {
                '时间': timezone.now().isoformat(),  # ISO格式的当前时间
                '操作': action,  # 操作名称
                '用户': user_info,  # 用户信息
                'IP地址': ip_address,  # 客户端IP
                '用户代理': user_agent,  # 浏览器/客户端信息
                '是否成功': '成功' if success else '失败',  # 操作结果
                '详细信息': details  # 额外的详细信息
            }
            
            # 步骤4：格式化日志消息并记录
            log_message = f"安全审计 - {json.dumps(log_entry, ensure_ascii=False)}"
            self.security_logger.info(log_message)
            
        except Exception as e:
            # 如果审计日志记录失败，记录错误但不影响业务流程
            self.error_logger.error(f"安全审计日志记录失败: {str(e)}")
    
    def performance_log(self, operation, duration, request, extra_data=None):
        """
        记录性能监控日志
        
        参数说明：
        - operation: 操作名称（如"API调用"、"数据库查询"、"文件上传"等）
        - duration: 操作耗时（秒），浮点数
        - request: Django请求对象，用于获取用户信息
        - extra_data: 可选的额外数据字典（如数据库查询数量、文件大小等）
        
        使用场景：
        - 监控API响应时间，及时发现性能瓶颈
        - 记录数据库查询性能，优化慢查询
        - 监控文件上传/下载速度
        - 记录复杂业务逻辑的执行时间
        
        特殊功能：
        - 自动检测慢操作（超过1秒）并发出警告
        - 记录详细的用户和时间信息
        
        示例：
        start_time = time.time()
        # 执行某个操作
        duration = time.time() - start_time
        logger_manager.performance_log("API查询用户列表", duration, request, {"count": 150})
        """
        try:
            # 步骤1：提取用户信息
            user_info = "匿名用户"
            if hasattr(request, 'session') and request.session.get('session_data'):
                session_data = request.session.get('session_data')
                user_info = session_data.get('username', '未知用户')
            
            # 步骤2：构建性能日志条目
            log_entry = {
                '时间': timezone.now().isoformat(),  # 操作时间
                '操作': operation,  # 操作名称
                '用户': user_info,  # 执行操作的用户
                '耗时(秒)': round(duration, 4),  # 操作耗时，保疙4位小数
                '额外数据': extra_data or {}  # 额外的监控数据
            }
            
            # 步骤3：格式化并记录性能日志
            log_message = f"性能监控 - {json.dumps(log_entry, ensure_ascii=False)}"
            self.performance_logger.info(log_message)
            
            # 步骤4：慢操作检测和警告
            # 如果操作耗时超过1秒，自动发出警告
            if duration > 1.0:
                self.warning(f"慢操作检测: {operation} 耗时 {duration:.3f}秒")
                
        except Exception as e:
            # 如果性能日志记录失败，记录错误但不影响业务流程
            self.error_logger.error(f"性能日志记录失败: {str(e)}")
    
    def db_query_log(self, query, duration, result_count=None):
        """
        记录数据库查询日志
        
        参数说明：
        - query: SQL查询语句或操作描述
        - duration: 查询耗时（秒），浮点数
        - result_count: 可选的查询结果数量（影响的行数或返回的记录数）
        
        使用场景：
        - 监控数据库查询性能
        - 记录复杂查询的执行情况
        - 记录数据更新、删除操作
        - 分析数据库使用模式和优化需求
        
        记录内容：
        - 查询执行时间
        - SQL语句或操作描述
        - 查询耗时（精确到4位小数）
        - 结果数量（如果提供）
        
        示例：
        start_time = time.time()
        users = User.objects.filter(department_id=1)
        duration = time.time() - start_time
        logger_manager.db_query_log(
            "SELECT * FROM users WHERE department_id = 1", 
            duration, 
            len(users)
        )
        """
        try:
            # 步骤1：构建数据库查询日志条目
            log_entry = {
                '时间': timezone.now().isoformat(),  # 查询执行时间
                '查询': query,  # SQL语句或操作描述
                '耗时(秒)': round(duration, 4),  # 查询耗时，保疙4位小数
                '结果数量': result_count  # 查询结果数量
            }
            
            # 步骤2：格式化并记录数据库日志
            log_message = f"数据库查询 - {json.dumps(log_entry, ensure_ascii=False)}"
            self.db_logger.info(log_message)
            
        except Exception as e:
            # 如果数据库日志记录失败，记录错误但不影响业务流程
            self.error_logger.error(f"数据库查询日志记录失败: {str(e)}")
    
    def _get_client_ip(self, request):
        """
        获取客户端真实IP地址
        
        参数说明：
        - request: Django请求对象
        
        功能说明：
        在代理服务器、负载均衡器或CDN环境中，
        客户端的真实IP可能被转发头信息所隐藏。
        此方法会优先检查X-Forwarded-For头，
        如果没有则使用REMOTE_ADDR。
        
        返回值：客户端真实IP地址字符串
        
        示例：
        - 直接访问：返回 "192.168.1.100"
        - 通过代理：返回真实客户端IP而非代理服务器IP
        """
        # 步骤1：检查X-Forwarded-For头（由代理服务器设置）
        # 这个头可能包含多个IP地址，用逗号分隔，第一个通常是真实客户端
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            # 获取第一个IP地址（去除空格）
            ip = x_forwarded_for.split(',')[0].strip()
        else:
            # 步骤2：如果没有X-Forwarded-For头，使用REMOTE_ADDR
            # REMOTE_ADDR是直接连接到服务器的客户端IP
            ip = request.META.get('REMOTE_ADDR')
        
        return ip


# 全局日志管理器实例
logger_manager = LoggerManager()


def log_api_call(api_name):
    """API调用日志装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            request = None
            
            # 获取request对象
            if len(args) > 1 and hasattr(args[1], 'META'):
                request = args[1]
            elif hasattr(args[0], 'request'):
                request = args[0].request
            
            try:
                result = func(*args, **kwargs)
                duration = time.time() - start_time
                
                # 记录API调用日志
                user_info = "匿名用户"
                if request and hasattr(request, 'session') and request.session.get('session_data'):
                    session_data = request.session.get('session_data')
                    user_info = session_data.get('username', '未知用户')
                
                logger_manager.info(f"API调用: {api_name} - 用户: {user_info} - 耗时: {duration:.3f}秒")
                
                # 记录性能日志
                if request:
                    logger_manager.performance_log(api_name, duration, request)
                
                return result
                
            except Exception as e:
                duration = time.time() - start_time
                user_info = "匿名用户"
                if request and hasattr(request, 'session') and request.session.get('session_data'):
                    session_data = request.session.get('session_data')
                    user_info = session_data.get('username', '未知用户')
                
                logger_manager.error(
                    f"API调用失败: {api_name} - 用户: {user_info} - 耗时: {duration:.3f}秒", 
                    exception=e
                )
                raise
        
        return wrapper
    return decorator


class LoggingMiddleware:
    """日志中间件，记录请求和响应信息"""
    
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        # 记录请求开始
        start_time = time.time()
        
        # 处理请求
        response = self.get_response(request)
        
        # 记录请求结束
        duration = time.time() - start_time
        
        # 记录访问日志
        try:
            user_info = "匿名用户"
            if hasattr(request, 'session') and request.session.get('session_data'):
                session_data = request.session.get('session_data')
                user_info = session_data.get('username', '未知用户')
            
            log_message = (
                f"请求处理 - 用户: {user_info} | "
                f"路径: {request.path} | "
                f"方法: {request.method} | "
                f"状态码: {response.status_code} | "
                f"耗时: {duration:.3f}秒"
            )
            
            logger_manager.info(log_message)
            
            # 记录慢请求
            if duration > 2.0:
                logger_manager.warning(f"慢请求检测: {request.path} 耗时 {duration:.3f}秒")
                
        except Exception as e:
            logger_manager.error(f"日志中间件记录失败: {str(e)}")
        
        return response


def log_user_action(action_name):
    """
    用户操作日志装饰器，专门用于记录安全审计日志
    
    参数说明：
    - action_name: 用户操作名称，用于安全审计记录
    
    自动记录内容：
    1. 操作名称和用户信息
    2. 操作时间和IP地址
    3. 操作结果（成功或失败）
    4. 异常信息（如果操作失败）
    
    使用场景：
    - 用户登录/登出操作
    - 密码修改、用户信息更新
    - 数据创建、修改、删除操作
    
    示例：
    @log_user_action("用户登录")
    def user_login(request):
        # 登录逻辑
        pass
    """
    def decorator(func):
        @wraps(func)  # 保持原函数元数据
        def wrapper(*args, **kwargs):
            request = None
            
            # 智能检测获取request对象
            if len(args) > 1 and hasattr(args[1], 'META'):
                request = args[1]
            elif hasattr(args[0], 'request'):
                request = args[0].request
            
            try:
                # 执行原函数
                result = func(*args, **kwargs)
                
                # 记录成功的安全审计日志
                if request:
                    logger_manager.security_audit(action_name, request, True, "用户操作成功")
                
                return result
                
            except Exception as e:
                # 记录失败的安全审计日志
                if request:
                    logger_manager.security_audit(action_name, request, False, f"用户操作失败: {str(e)}")
                # 重新抛出异常
                raise
        
        return wrapper
    return decorator